package com.ruoyi.learn.java.design.pattern.creator;

/**
 * 原型模式 (Prototype Pattern) 示例：克隆文档对象。
 */
public class PrototypePatternDemo {

    //region ========== 1. 具体原型 (Concrete Prototype) - Author ==========

    /**
     * 作者类 (Author)
     * 这是一个引用类型，需要在克隆时特别注意深拷贝。
     */
    public static class Author implements Cloneable {
        private String name;
        private int age;

        public Author(String name, int age) {
            this.name = name;
            this.age = age;
        }

        // Getter 和 Setter
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public int getAge() { return age; }
        public void setAge(int age) { this.age = age; }

        @Override
        public String toString() {
            return "Author{name='" + name + "', age=" + age + '}';
        }

        /**
         * 重写 clone() 方法，实现 Author 对象的深拷贝。
         * 注意：Author 本身只包含基本类型和 String，String 是不可变的，所以直接调用 super.clone() 即可。
         * 如果 Author 包含其他可变对象引用，则需要递归克隆。
         */
        @Override
        public Author clone() {
            try {
                return (Author) super.clone();
            } catch (CloneNotSupportedException e) {
                // Cloneable 接口已实现，理论上不会抛出此异常
                throw new AssertionError();
            }
        }
    }

    //endregion

    //region ========== 2. 具体原型 (Concrete Prototype) - Document ==========

    /**
     * 文档类 (Document) - 具体原型
     * 实现 Cloneable 接口，并重写 clone() 方法。
     */
    public static class Document implements Cloneable {
        private String title;
        private String content;
        private Author author; // 引用类型

        public Document(String title, String content, Author author) {
            this.title = title;
            this.content = content;
            this.author = author;
        }

        // Getter 和 Setter
        public String getTitle() { return title; }
        public void setTitle(String title) { this.title = title; }
        public String getContent() { return content; }
        public void setContent(String content) { this.content = content; }
        public Author getAuthor() { return author; }
        public void setAuthor(Author author) { this.author = author; }

        @Override
        public String toString() {
            return "Document{" +
                    "title='" + title + '\'' +
                    ", content='" + content + '\'' +
                    ", author=" + author +
                    '}';
        }

        /**
         * 重写 clone() 方法，实现 Document 对象的深拷贝。
         * 关键：对于引用类型的字段（如 author），需要递归调用其 clone() 方法，
         * 否则只是复制了引用（浅拷贝），会导致原对象和克隆对象共享同一个 Author 实例。
         */
        @Override
        public Document clone() {
            try {
                Document cloned = (Document) super.clone(); // 先克隆基本类型和 String
                // 对引用类型字段进行深拷贝
                if (this.author != null) {
                    cloned.author = this.author.clone(); // 调用 Author 的 clone()
                }
                return cloned;
            } catch (CloneNotSupportedException e) {
                // Cloneable 接口已实现，理论上不会抛出此异常
                throw new AssertionError();
            }
        }
    }

    //endregion

    //region ========== 3. 原型管理器 (Prototype Registry) - 可选组件 ==========

    /**
     * 原型管理器 (Prototype Registry)
     * 可选角色，用于管理一组可被克隆的原型对象。
     * 通常使用 Map 存储不同标识的原型。
     */
    public static class DocumentPrototypeRegistry {
        private java.util.Map<String, Document> prototypes = new java.util.HashMap<>();

        public void addPrototype(String key, Document prototype) {
            prototypes.put(key, prototype);
        }

        public Document getPrototype(String key) {
            Document prototype = prototypes.get(key);
            return prototype != null ? prototype.clone() : null; // 返回克隆体，不暴露原型
        }

        public void removePrototype(String key) {
            prototypes.remove(key);
        }
    }

    //endregion

    //region ========== 4. 主函数 (Main) - 演示原型模式 ==========

    public static void main(String[] args) {
        System.out.println("=== 原型模式演示 ===\n");

        // 1. 创建一个原型对象 (已配置好的文档)
        Author originalAuthor = new Author("张三", 30);
        Document originalDocument = new Document("设计模式详解", "这是一篇关于设计模式的详细文章...", originalAuthor);

        System.out.println("原始文档: " + originalDocument);
        System.out.println("原始文档的 Author hashCode: " + originalDocument.getAuthor().hashCode());

        // 2. 使用 clone() 方法创建新对象 (克隆体)
        Document clonedDocument = originalDocument.clone();

        System.out.println("\n克隆的文档: " + clonedDocument);
        System.out.println("克隆文档的 Author hashCode: " + clonedDocument.getAuthor().hashCode());

        // 3. 验证深拷贝效果
        System.out.println("\n=== 验证深拷贝 ===");
        System.out.println("原始文档和克隆文档是否为同一对象? " + (originalDocument == clonedDocument)); // false
        System.out.println("原始文档的 Author 和克隆文档的 Author 是否为同一对象? " +
                (originalDocument.getAuthor() == clonedDocument.getAuthor())); // false

        // 修改克隆文档的作者信息
        clonedDocument.getAuthor().setName("李四");
        clonedDocument.getAuthor().setAge(25);
        clonedDocument.setTitle("克隆版: " + clonedDocument.getTitle());

        System.out.println("\n修改克隆文档后:");
        System.out.println("原始文档: " + originalDocument); // 原始文档的作者信息未改变
        System.out.println("克隆文档: " + clonedDocument);   // 克隆文档的作者信息已改变

        // 4. 演示原型管理器 (可选)
        System.out.println("\n=== 使用原型管理器 ===");
        DocumentPrototypeRegistry registry = new DocumentPrototypeRegistry();
        registry.addPrototype("techDoc", originalDocument);

        // 从管理器获取克隆体
        Document techDoc1 = registry.getPrototype("techDoc");
        Document techDoc2 = registry.getPrototype("techDoc");

        System.out.println("从管理器获取的文档1: " + techDoc1);
        System.out.println("从管理器获取的文档2: " + techDoc2);
        System.out.println("techDoc1 和 techDoc2 是否为同一对象? " + (techDoc1 == techDoc2)); // false, 都是独立克隆体
        System.out.println("techDoc1 的 Author 和 techDoc2 的 Author 是否为同一对象? " +
                (techDoc1.getAuthor() == techDoc2.getAuthor())); // false, 深拷贝
    }

    //endregion
}
